﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using QuickGraph;
using QuickGraph.Serialization;
using System.IO;
using System.Reflection;
using GraphMLComplexType;
using EdgeWithAttributes;

namespace QuickGraphBuilder
{
    public class GraphMLBuilder
    {
        public static UndirectedGraph<TParentType, EdgeWithAttributes.EdgeWithAttributes<TParentType>> Build<TParentType>(String graphml, String pathToSaveDLL)
            where TParentType : IGraphMLComplexType, new()
        {
            StringReader reader = new StringReader(graphml);
            Type VertexExtension = ComplexTypeBuilder<String, String, TParentType>.CreateType(reader, pathToSaveDLL);
            reader.Close();

            //Type EdgeVertexExtension = System.Reflection.Assembly.GetAssembly(typeof(EdgeWithAttributes<String>)).GetType("EdgeWithAttributes.EdgeWithAttributes`1").MakeGenericType(new Type[] { VertexExtension });
            Type EdgeVertexExtension = System.Reflection.Assembly.GetAssembly(typeof(Edge<String>)).GetType("QuickGraph.Edge`1").MakeGenericType(new Type[] { VertexExtension });
            MethodInfo loadGraph = MethodBase.GetCurrentMethod().DeclaringType.GetMethod("LoadGraph");
            Type returnType = loadGraph.ReturnType;
            MethodInfo info = loadGraph.MakeGenericMethod(new Type[] { VertexExtension, EdgeVertexExtension, typeof(TParentType) });
            //TODO: add return type cast
            Object obj = Activator.CreateInstance(VertexExtension);

            UndirectedGraph<TParentType, Edge<TParentType>> ela = (UndirectedGraph<TParentType, Edge<TParentType>>)info.Invoke(null, new object[] { graphml });
            UndirectedGraph<TParentType, EdgeWithAttributes.EdgeWithAttributes<TParentType>> ret = new UndirectedGraph<TParentType, EdgeWithAttributes.EdgeWithAttributes<TParentType>>();
            foreach(var c in ela.Vertices)
            {
                ret.AddVertex(c);

            }
            foreach (var c in ela.Edges)
            {
                EdgeWithAttributes.EdgeWithAttributes<TParentType> tmp = new EdgeWithAttributes.EdgeWithAttributes<TParentType>(c.Source,c.Target);
                tmp.Condition = 0;
                ret.AddEdge(tmp);
            }


            return ret;
            //return (UndirectedGraph<TParentType, EdgeWithAttributes.EdgeWithAttributes<TParentType>>)info.Invoke(null, new object[] { graphml });
        }

        public static TEdge CreateEdge<TVertex, TEdge>(TVertex source, TVertex target, string desc)
            where TVertex : IGraphMLComplexType, new()
            where TEdge : Edge<TVertex>
        {
            TEdge edge = (TEdge)new Edge<TVertex>(source, target);
            return edge;
        }

        public static UndirectedGraph<TParentType, Edge<TParentType>> LoadGraph<TVertex, TEdge, TParentType>(string graphmlFile)
            where TParentType : IGraphMLComplexType, new()
            where TVertex : TParentType, IGraphMLComplexType, new()
            where TEdge : Edge<TVertex>
        {
            //Console.WriteLine(graphmlFile);
            //TODO: add loggin
            StringReader reader = new StringReader(graphmlFile);
            XmlTextReader xmlReader = new XmlTextReader(reader);
            var g = new UndirectedGraph<TVertex, TEdge>();

            QuickGraph.Serialization.GraphMLDeserializer<TVertex, TEdge, UndirectedGraph<TVertex, TEdge>> deserializer = new QuickGraph.Serialization.GraphMLDeserializer<TVertex, TEdge, UndirectedGraph<TVertex, TEdge>>();
            deserializer.Deserialize(
                xmlReader,
                g,
                id => new TVertex(),
                new IdentifiableEdgeFactory<TVertex, TEdge>(CreateEdge<TVertex, TEdge>)
                );


            reader.Close();

            var g1 = new UndirectedGraph<TParentType, Edge<TParentType>>(true);//false, g.Vertices.Count(), g.Edges.Count());
            Dictionary<TVertex, TParentType> map = new Dictionary<TVertex, TParentType>();
            foreach (var item in g.Vertices)
            {
                TParentType vertex = new TParentType();
                map.Add(item, vertex);
                vertex.Keys = item.Keys;
                g1.AddVertex(vertex);
            }

            foreach (var item in g.Edges)
            {
                g1.AddEdge(new Edge<TParentType>(map[item.Source], map[item.Target]));
            }


            return g1;
        }
    }
}
